<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <title>Loader - 为企业级框架和应用而生</title>
  <meta charset="utf-8">
  <meta name="description" content="index.description">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <link rel="icon" href="/images/favicon.png" type="image/x-icon">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.css" />
<link rel="stylesheet" href="/css/index.css">

    <script>
    !function(t,e,a,r,c){t.TracertCmdCache=t.TracertCmdCache||[],t[c]=window[c]||
      {_isInit:!0,call:function(){t.TracertCmdCache.push(arguments)},
      start:function(t){this.call('start',t)}},t[c].l=new Date;
      var n=e.createElement(a),s=e.getElementsByTagName(a)[0];
      n.async=!0,n.src=r,s.parentNode.insertBefore(n,s)}
    (window,document,'script','https://tracert.alipay.com/tracert.js','Tracert');
      Tracert.start({
        plugins: [ 'BucName' ],
        spmAPos: 'a454',
        spmBPos: 'b4893',
      });
    </script>
  
</head>
<body>
  <div class="nav" >
  <header>
    <a href="/zh-cn/" class="nav-logo leftpadding" alt="egg"><img src="https://zos.alipayobjects.com/rmsportal/VTcUYAaoKqXyHJbLAPyF.svg"></a>
    <ul class="nav-item">
      <li>
        <form id="search-form">
          <input type="text" id="search-query" class="search-query st-default-search-input">
        </form>
      </li>
      <li><a href="/zh-cn/intro/" alt="指南">指南</a></li><li><a href="/api/" alt="API">API</a></li><li><a href="/zh-cn/tutorials/index.html" alt="教程">教程</a></li><li><a href="https://github.com/search?q=topic%3Aegg-plugin&type=Repositories" alt="插件">插件</a></li><li><a href="https://github.com/eggjs/egg/releases" alt="发布日志">发布日志</a></li>
      
      
        <li class="translations">
          <a class="nav-link">Translations</a>
          <span class="arrow"></span><ul id="dropdownContent" class="dropdown-content"><li><a id="en" href="/en/advanced/loader.html" >English</a></li><li><a id="zh-cn" href="/zh-cn/advanced/loader.html" style="color: #22ab28">中文</a></li></ul>
        </li>
      
      <li><iframe src="https://ghbtns.com/github-btn.html?user=eggjs&repo=egg&type=star&count=true" frameborder="0" scrolling="0" width="150px" height="20px"></iframe></li>
    </ul>
    <a id="mobileTrigger" href="#" class="mobile-trigger">
      <ul>
        <li></li>
        <li></li>
        <li></li>
      </ul>
    </a>
  </header>
</div>
  <div id="container" class="container">
    <div class="page-main">
  <article class="markdown-body">
    <h1>Loader</h1>
    <p>Egg 在 Koa 的基础上进行增强最重要的就是基于一定的约定，根据功能差异将代码放到不同的目录下管理，对整体团队的开发成本提升有着明显的效果。Loader 实现了这套约定，并抽象了很多底层 API 可以进一步扩展。</p>
<h2 id="应用-框架和插件"><a class="markdown-anchor" href="#应用-框架和插件">#</a> 应用、框架和插件</h2>
<p>Egg 是一个底层框架，应用可以直接使用，但 Egg 本身的插件比较少，应用需要自己配置插件增加各种特性，比如 MySQL。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 应用配置</span></span><br><span class="line"><span class="comment">// package.json</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">"dependencies"</span>: &#123;</span><br><span class="line">    <span class="string">"egg"</span>: <span class="string">"^2.0.0"</span>,</span><br><span class="line">    <span class="string">"egg-mysql"</span>: <span class="string">"^3.0.0"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// config/plugin.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  mysql: &#123;</span><br><span class="line">    enable: <span class="literal">true</span>,</span><br><span class="line">    package: <span class="string">'egg-mysql'</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>当应用达到一定数量，我们会发现大部分应用的配置都是类似的，这时可以基于 Egg 扩展出一个框架，应用的配置就会简化很多。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 框架配置</span></span><br><span class="line"><span class="comment">// package.json</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">"name"</span>: <span class="string">"framework1"</span>,</span><br><span class="line">  <span class="string">"version"</span>: <span class="string">"1.0.0"</span>,</span><br><span class="line">  <span class="string">"dependencies"</span>: &#123;</span><br><span class="line">    <span class="string">"egg-mysql"</span>: <span class="string">"^3.0.0"</span>,</span><br><span class="line">    <span class="string">"egg-view-nunjucks"</span>: <span class="string">"^2.0.0"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// config/plugin.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  mysql: &#123;</span><br><span class="line">    enable: <span class="literal">false</span>,</span><br><span class="line">    package: <span class="string">'egg-mysql'</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">  view: &#123;</span><br><span class="line">    enable: <span class="literal">false</span>,</span><br><span class="line">    package: <span class="string">'egg-view-nunjucks'</span>,</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 应用配置</span></span><br><span class="line"><span class="comment">// package.json</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">"dependencies"</span>: &#123;</span><br><span class="line">    <span class="string">"framework1"</span>: <span class="string">"^1.0.0"</span>,</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// config/plugin.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// 开启插件</span></span><br><span class="line">  mysql: <span class="literal">true</span>,</span><br><span class="line">  view: <span class="literal">true</span>,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>从上面的使用场景可以看到应用、插件和框架三者之间的关系。</p>
<ul>
<li>我们在应用中完成业务，需要指定一个框架才能运行起来，当需要某个特性场景的功能时可以配置插件（比如 MySQL）。</li>
<li>插件只完成特定功能，当两个独立的功能有互相依赖时，还是分开两个插件，但需要配置依赖。</li>
<li>框架是一个启动器（默认就是 Egg），必须有它才能运行起来。框架还是一个封装器，将插件的功能聚合起来统一提供，框架也可以配置插件。</li>
<li>在框架的基础上还可以扩展出新的框架，也就是说<strong>框架是可以无限级继承的</strong>，有点像类的继承。</li>
</ul>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">+-----------------------------------+--------+</span><br><span class="line">|      app1, app2, app3, app4       |        |</span><br><span class="line">+-----+--------------+--------------+        |</span><br><span class="line">|     |              |  framework3  |        |</span><br><span class="line">+     |  framework1  +--------------+ plugin |</span><br><span class="line">|     |              |  framework2  |        |</span><br><span class="line">+     +--------------+--------------+        |</span><br><span class="line">|                   Egg             |        |</span><br><span class="line">+-----------------------------------+--------|</span><br><span class="line">|                   Koa                      |</span><br><span class="line">+-----------------------------------+--------+</span><br></pre></td></tr></table></figure>
<h2 id="loadunit"><a class="markdown-anchor" href="#loadunit">#</a> loadUnit</h2>
<p>Egg 将应用、框架和插件都称为加载单元（loadUnit），因为在代码结构上几乎没有什么差异，下面是目录结构</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">loadUnit</span><br><span class="line">├── package.json</span><br><span class="line">├── app.js</span><br><span class="line">├── agent.js</span><br><span class="line">├── app</span><br><span class="line">│   ├── extend</span><br><span class="line">│   |   ├── helper.js</span><br><span class="line">│   |   ├── request.js</span><br><span class="line">│   |   ├── response.js</span><br><span class="line">│   |   ├── context.js</span><br><span class="line">│   |   ├── application.js</span><br><span class="line">│   |   └── agent.js</span><br><span class="line">│   ├── service</span><br><span class="line">│   ├── middleware</span><br><span class="line">│   └── router.js</span><br><span class="line">└── config</span><br><span class="line">    ├── config.default.js</span><br><span class="line">    ├── config.prod.js</span><br><span class="line">    ├── config.test.js</span><br><span class="line">    ├── config.local.js</span><br><span class="line">    └── config.unittest.js</span><br></pre></td></tr></table></figure>
<p>不过还存在着一些差异</p>
<table>
<thead>
<tr>
<th>文件</th>
<th>应用</th>
<th>框架</th>
<th>插件</th>
</tr>
</thead>
<tbody>
<tr>
<td>app/router.js</td>
<td>✔︎</td>
<td></td>
<td></td>
</tr>
<tr>
<td>app/controller</td>
<td>✔︎</td>
<td></td>
<td></td>
</tr>
<tr>
<td>app/middleware</td>
<td>✔︎</td>
<td>✔︎</td>
<td>✔︎</td>
</tr>
<tr>
<td>app/service</td>
<td>✔︎</td>
<td>✔︎</td>
<td>✔︎</td>
</tr>
<tr>
<td>app/extend</td>
<td>✔︎</td>
<td>✔︎</td>
<td>✔︎</td>
</tr>
<tr>
<td>app.js</td>
<td>✔︎</td>
<td>✔︎</td>
<td>✔︎</td>
</tr>
<tr>
<td>agent.js</td>
<td>✔︎</td>
<td>✔︎</td>
<td>✔︎</td>
</tr>
<tr>
<td>config/config.{env}.js</td>
<td>✔︎</td>
<td>✔︎</td>
<td>✔︎</td>
</tr>
<tr>
<td>config/plugin.js</td>
<td>✔︎</td>
<td>✔︎</td>
<td></td>
</tr>
<tr>
<td>package.json</td>
<td>✔︎</td>
<td>✔︎</td>
<td>✔︎</td>
</tr>
</tbody>
</table>
<p>在加载过程中，Egg 会遍历所有的 loadUnit 加载上述的文件（应用、框架、插件各有不同），加载时有一定的优先级</p>
<ul>
<li>按插件 =&gt; 框架 =&gt; 应用依次加载</li>
<li>插件之间的顺序由依赖关系决定，被依赖方先加载，无依赖按 object key 配置顺序加载，具体可以查看<a href="./plugin.html">插件章节</a></li>
<li>框架按继承顺序加载，越底层越先加载。</li>
</ul>
<p>比如有这样一个应用配置了如下依赖</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">app</span><br><span class="line">| ├── plugin2 (依赖 plugin3)</span><br><span class="line">| └── plugin3</span><br><span class="line">└── framework1</span><br><span class="line">    | └── plugin1</span><br><span class="line">    └── egg</span><br></pre></td></tr></table></figure>
<p>最终的加载顺序为</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">=&gt; plugin1</span><br><span class="line">=&gt; plugin3</span><br><span class="line">=&gt; plugin2</span><br><span class="line">=&gt; egg</span><br><span class="line">=&gt; framework1</span><br><span class="line">=&gt; app</span><br></pre></td></tr></table></figure>
<p>plugin1 为 framework1 依赖的插件，配置合并后 object key 的顺序会优先于 plugin2/plugin3。因为 plugin2 和 plugin3 的依赖关系，所以交换了位置。framework1 继承了 egg，顺序会晚于 egg。应用最后加载。</p>
<p>请查看 <a href="https://github.com/eggjs/egg-core/blob/65ea778a4f2156a9cebd3951dac12c4f9455e636/lib/loader/egg_loader.js#L233" target="_blank" rel="noopener">Loader.getLoadUnits</a> 方法</p>
<h3 id="文件顺序"><a class="markdown-anchor" href="#文件顺序">#</a> 文件顺序</h3>
<p>上面已经列出了默认会加载的文件，Egg 会按如下文件顺序加载，每个文件或目录再根据 loadUnit 的顺序去加载（应用、框架、插件各有不同）。</p>
<ul>
<li>加载 <a href="./plugin.html">plugin</a>，找到应用和框架，加载 <code>config/plugin.js</code></li>
<li>加载 <a href="../basics/config.html">config</a>，遍历 loadUnit 加载 <code>config/config.{env}.js</code></li>
<li>加载 <a href="../basics/extend.html">extend</a>，遍历 loadUnit 加载 <code>app/extend/xx.js</code></li>
<li><a href="../basics/app-start.html">自定义初始化</a>，遍历 loadUnit 加载 <code>app.js</code> 和 <code>agent.js</code></li>
<li>加载 <a href="../basics/service.html">service</a>，遍历 loadUnit 加载 <code>app/service</code> 目录</li>
<li>加载 <a href="../basics/middleware.html">middleware</a>，遍历 loadUnit 加载 <code>app/middleware</code> 目录</li>
<li>加载 <a href="../basics/controller.html">controller</a>，加载应用的 <code>app/controller</code> 目录</li>
<li>加载 <a href="../basics/router.html">router</a>，加载应用的 <code>app/router.js</code></li>
</ul>
<p>注意</p>
<ul>
<li>加载时如果遇到同名的会覆盖，比如想要覆盖 <code>ctx.ip</code> 可以直接在应用的 <code>app/extend/context.js</code> 定义 ip 就可以了。</li>
<li>应用完整启动顺序查看<a href="./framework.html">框架开发</a></li>
</ul>
<h3 id="文件加载规则"><a class="markdown-anchor" href="#文件加载规则">#</a> 文件加载规则</h3>
<p>框架在加载文件时会进行转换，因为文件命名风格和 API 风格存在差异。我们推荐文件使用下划线，而 API 使用驼峰。比如 <code>app/service/user_info.js</code> 会转换成 <code>app.service.userInfo</code>。</p>
<p>框架也支持连字符和驼峰的方式</p>
<ul>
<li><code>app/service/user-info.js</code> =&gt; <code>app.service.userInfo</code></li>
<li><code>app/service/userInfo.js</code> =&gt; <code>app.service.userInfo</code></li>
</ul>
<p>Loader 还提供了 <a href="#caseStyle-string">caseStyle</a> 强制指定首字母大小写，比如加载 model 时 API 首字母大写，<code>app/model/user.js</code> =&gt; <code>app.model.User</code>，就可以指定 <code>caseStyle: 'upper'</code>。</p>
<h2 id="扩展-loader"><a class="markdown-anchor" href="#扩展-loader">#</a> 扩展 Loader</h2>
<p><a href="https://github.com/eggjs/egg-core/blob/master/lib/loader/egg_loader.js" target="_blank" rel="noopener">Loader</a> 是一个基类，并根据文件加载的规则提供了一些内置的方法，但基本本身并不会去调用，而是由继承类调用。</p>
<ul>
<li>loadPlugin()</li>
<li>loadConfig()</li>
<li>loadAgentExtend()</li>
<li>loadApplicationExtend()</li>
<li>loadRequestExtend()</li>
<li>loadResponseExtend()</li>
<li>loadContextExtend()</li>
<li>loadHelperExtend()</li>
<li>loadCustomAgent()</li>
<li>loadCustomApp()</li>
<li>loadService()</li>
<li>loadMiddleware()</li>
<li>loadController()</li>
<li>loadRouter()</li>
</ul>
<p>Egg 基于 Loader 实现了 <a href="https://github.com/eggjs/egg/blob/master/lib/loader/app_worker_loader.js" target="_blank" rel="noopener">AppWorkerLoader</a> 和 <a href="https://github.com/eggjs/egg/blob/master/lib/loader/agent_worker_loader.js" target="_blank" rel="noopener">AgentWorkerLoader</a>，上层框架基于这两个类来扩展，<strong>Loader 的扩展只能在框架进行</strong>。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 自定义 AppWorkerLoader</span></span><br><span class="line"><span class="comment">// lib/framework.js</span></span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>);</span><br><span class="line"><span class="keyword">const</span> egg = <span class="built_in">require</span>(<span class="string">'egg'</span>);</span><br><span class="line"><span class="keyword">const</span> EGG_PATH = <span class="built_in">Symbol</span>.for(<span class="string">'egg#eggPath'</span>);</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">YadanAppWorkerLoader</span> <span class="keyword">extends</span> <span class="title">egg</span>.<span class="title">AppWorkerLoader</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(opt) &#123;</span><br><span class="line">    <span class="keyword">super</span>(opt);</span><br><span class="line">    <span class="comment">// 自定义初始化</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  loadConfig() &#123;</span><br><span class="line">    <span class="keyword">super</span>.loadConfig();</span><br><span class="line">    <span class="comment">// 对 config 进行处理</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  load() &#123;</span><br><span class="line">    <span class="keyword">super</span>.load();</span><br><span class="line">    <span class="comment">// 自定义加载其他目录</span></span><br><span class="line">    <span class="comment">// 或对已加载的文件进行处理</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Application</span> <span class="keyword">extends</span> <span class="title">egg</span>.<span class="title">Application</span> </span>&#123;</span><br><span class="line">  get [EGG_PATH]() &#123;</span><br><span class="line">    <span class="keyword">return</span> path.dirname(__dirname);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 覆盖 Egg 的 Loader，启动时使用这个 Loader</span></span><br><span class="line">  get [EGG_LOADER]() &#123;</span><br><span class="line">    <span class="keyword">return</span> YadanAppWorkerLoader;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="built_in">Object</span>.assign(egg, &#123;</span><br><span class="line">  Application,</span><br><span class="line">  <span class="comment">// 自定义的 Loader 也需要 export，上层框架需要基于这个扩展</span></span><br><span class="line">  AppWorkerLoader: YadanAppWorkerLoader,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>通过 Loader 提供的这些 API，可以很方便的定制团队的自定义加载，如 <code>this.model.xx</code>， <code>app/extend/filter.js</code> 等等。</p>
<p>以上只是说明 Loader 的写法，具体可以查看<a href="./framework.html">框架开发</a>。</p>
<h2 id="loader-api"><a class="markdown-anchor" href="#loader-api">#</a> Loader API</h2>
<p>Loader 还提供一些底层的 API，在扩展时可以简化代码，全部 API 请<a href="https://github.com/eggjs/egg-core#eggloader" target="_blank" rel="noopener">查看</a></p>
<h3 id="loadfile"><a class="markdown-anchor" href="#loadfile">#</a> loadFile</h3>
<p>用于加载一个文件，比如加载 <code>app.js</code> 就是使用这个方法。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/xx.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(app.config);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// app.js</span></span><br><span class="line"><span class="comment">// 以 app/xx.js 为例，我们可以在 app.js 加载这个文件</span></span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>);</span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  app.loader.loadFile(path.join(app.config.baseDir, <span class="string">'app/xx.js'</span>));</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>如果文件 export 一个函数会被调用，并将 app 作为参数，否则直接使用这个值。</p>
<h3 id="loadtoapp"><a class="markdown-anchor" href="#loadtoapp">#</a> loadToApp</h3>
<p>用于加载一个目录下的文件到 app，比如 <code>app/controller/home.js</code> 会加载到 <code>app.controller.home</code>。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app.js</span></span><br><span class="line"><span class="comment">// 以下只是示例，加载 controller 请用 loadController</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> directory = path.join(app.config.baseDir, <span class="string">'app/controller'</span>);</span><br><span class="line">  app.loader.loadToApp(directory, <span class="string">'controller'</span>);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>一共有三个参数 <code>loadToApp(directory, property, LoaderOptions)</code></p>
<ol>
<li>directory 可以为 String 或 Array，Loader 会从这些目录加载文件</li>
<li>property 为 app 的属性</li>
<li><a href="#LoaderOptions">LoaderOptions</a> 为一些配置</li>
</ol>
<h3 id="loadtocontext"><a class="markdown-anchor" href="#loadtocontext">#</a> loadToContext</h3>
<p>与 loadToApp 有一点差异，loadToContext 是加载到 ctx 上而非 app，而且是懒加载。加载时会将文件都放到一个临时对象上，在调用 ctx API 时才实例化对象。</p>
<p>比如 service 的加载就是使用这种模式</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 以下为示例，请使用 loadService</span></span><br><span class="line"><span class="comment">// app/service/user.js</span></span><br><span class="line"><span class="keyword">const</span> Service = <span class="built_in">require</span>(<span class="string">'egg'</span>).Service;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">UserService</span> <span class="keyword">extends</span> <span class="title">Service</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">module</span>.exports = UserService;</span><br><span class="line"></span><br><span class="line"><span class="comment">// app.js</span></span><br><span class="line"><span class="comment">// 获取所有的 loadUnit</span></span><br><span class="line"><span class="keyword">const</span> servicePaths = app.loader.getLoadUnits().map(<span class="function"><span class="params">unit</span> =&gt;</span> path.join(unit.path, <span class="string">'app/service'</span>));</span><br><span class="line"></span><br><span class="line">app.loader.loadToContext(servicePaths, <span class="string">'service'</span>, &#123;</span><br><span class="line">  <span class="comment">// service 需要继承 app.Service，所以要拿到 app 参数</span></span><br><span class="line">  <span class="comment">// 设置 call 在加载时会调用函数返回 UserService</span></span><br><span class="line">  call: <span class="literal">true</span>,</span><br><span class="line">  <span class="comment">// 将文件加载到 app.serviceClasses</span></span><br><span class="line">  fieldClass: <span class="string">'serviceClasses'</span>,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>文件加载后 <code>app.serviceClasses.user</code> 就是 UserService，当调用 <code>ctx.service.user</code> 时会实例化 UserService，
所以这个类只有每次请求中首次访问时才会实例化，实例化后会被缓存，同一个请求多次调用也只会实例化一次。</p>
<h3 id="loaderoptions"><a class="markdown-anchor" href="#loaderoptions">#</a> LoaderOptions</h3>
<h4 id="ignore-string"><a class="markdown-anchor" href="#ignore-string">#</a> ignore [String]</h4>
<p>ignore 可以忽略一些文件，支持 glob，默认为空</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">app.loader.loadToApp(directory, <span class="string">'controller'</span>, &#123;</span><br><span class="line">  <span class="comment">// 忽略 app/controller/util 下的文件</span></span><br><span class="line">  ignore: <span class="string">'util/**'</span>,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h4 id="initializer-function"><a class="markdown-anchor" href="#initializer-function">#</a> initializer [Function]</h4>
<p>对每个文件 export 出来的值进行处理，默认为空</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/model/user.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="class"><span class="keyword">class</span> <span class="title">User</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(app, path) &#123;&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 从 app/model 目录加载，加载时可做一些初始化处理</span></span><br><span class="line"><span class="keyword">const</span> directory = path.join(app.config.baseDir, <span class="string">'app/model'</span>);</span><br><span class="line">app.loader.loadToApp(directory, <span class="string">'model'</span>, &#123;</span><br><span class="line">  initializer(model, opt) &#123;</span><br><span class="line">    <span class="comment">// 第一个参数为 export 的对象</span></span><br><span class="line">    <span class="comment">// 第二个参数为一个对象，只包含当前文件的路径</span></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> model(app, opt.path);</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h4 id="casestyle-string"><a class="markdown-anchor" href="#casestyle-string">#</a> caseStyle [String]</h4>
<p>文件的转换规则，可选为 <code>camel</code>，<code>upper</code>，<code>lower</code>，默认为 <code>camel</code>。</p>
<p>三者都会将文件名转换成驼峰，但是对于首字母的处理有所不同。</p>
<ul>
<li><code>camel</code>：首字母不变。</li>
<li><code>upper</code>：首字母大写。</li>
<li><code>lower</code>：首字母小写。</li>
</ul>
<p>在加载不同文件时配置不同</p>
<table>
<thead>
<tr>
<th>文件</th>
<th>配置</th>
</tr>
</thead>
<tbody>
<tr>
<td>app/controller</td>
<td>lower</td>
</tr>
<tr>
<td>app/middleware</td>
<td>lower</td>
</tr>
<tr>
<td>app/service</td>
<td>lower</td>
</tr>
</tbody>
</table>
<h4 id="override-boolean"><a class="markdown-anchor" href="#override-boolean">#</a> override [Boolean]</h4>
<p>遇到已经存在的文件时是直接覆盖还是抛出异常，默认为 false</p>
<p>比如同时加载应用和插件的 <code>app/service/user.js</code> 文件，如果为 true 应用会覆盖插件的，否则加载应用的文件时会报错。</p>
<p>在加载不同文件时配置不同</p>
<table>
<thead>
<tr>
<th>文件</th>
<th>配置</th>
</tr>
</thead>
<tbody>
<tr>
<td>app/controller</td>
<td>true</td>
</tr>
<tr>
<td>app/middleware</td>
<td>false</td>
</tr>
<tr>
<td>app/service</td>
<td>false</td>
</tr>
</tbody>
</table>
<h4 id="call-boolean"><a class="markdown-anchor" href="#call-boolean">#</a> call [Boolean]</h4>
<p>当 export 的对象为函数时则调用，并获取返回值，默认为 true</p>
<p>在加载不同文件时配置不同</p>
<table>
<thead>
<tr>
<th>文件</th>
<th>配置</th>
</tr>
</thead>
<tbody>
<tr>
<td>app/controller</td>
<td>true</td>
</tr>
<tr>
<td>app/middleware</td>
<td>false</td>
</tr>
<tr>
<td>app/service</td>
<td>true</td>
</tr>
</tbody>
</table>

  </article>
  <aside id="mobileAside" class="toc">
  <div class="mobile-menu">
    <ul>
      <li><a href="/zh-cn/intro/" alt="指南">指南</a></li><li><a href="/api/" alt="API">API</a></li><li><a href="/zh-cn/tutorials/index.html" alt="教程">教程</a></li><li><a href="https://github.com/search?q=topic%3Aegg-plugin&type=Repositories" alt="插件">插件</a></li><li><a href="https://github.com/eggjs/egg/releases" alt="发布日志">发布日志</a></li>
      
      
        <li class="translations">
          <a class="nav-link">Translations</a>
          <span class="arrow"></span><ul id="dropdownContent" class="dropdown-content"><li><a id="en" href="/en/advanced/loader.html" >English</a></li><li><a id="zh-cn" href="/zh-cn/advanced/loader.html" style="color: #22ab28">中文</a></li></ul>
        </li>
      
    </ul>
  </div>
  <dl><dt id="title-Intro" style="cursor: pointer;" class="aside-title">新手指南<a id="collapse-icon-Intro" class="icon opend"></a></dt><dd id=panel-Intro><ul><li><a href="/zh-cn/intro/index.html" class="menu-link">Egg.js 是什么?</a></li><li><a href="/zh-cn/intro/egg-and-koa.html" class="menu-link">Egg.js 和 Koa</a></li><li><a href="/zh-cn/intro/quickstart.html" class="menu-link">快速入门</a></li><li><a href="/zh-cn/tutorials/progressive.html" class="menu-link">渐进式开发</a></li><li><a href="/zh-cn/migration.html" class="menu-link">2.x 升级指南</a></li></ul></dd><dt id="title-Basics" style="cursor: pointer;" class="aside-title">基础功能<a id="collapse-icon-Basics" class="icon opend"></a></dt><dd id=panel-Basics><ul><li><a href="/zh-cn/basics/structure.html" class="menu-link">目录结构</a></li><li><a href="/zh-cn/basics/objects.html" class="menu-link">内置对象</a></li><li><a href="/zh-cn/basics/env.html" class="menu-link">运行环境</a></li><li><a href="/zh-cn/basics/config.html" class="menu-link">配置</a></li><li><a href="/zh-cn/basics/middleware.html" class="menu-link">中间件</a></li><li><a href="/zh-cn/basics/router.html" class="menu-link">Router</a></li><li><a href="/zh-cn/basics/controller.html" class="menu-link">Controller</a></li><li><a href="/zh-cn/basics/service.html" class="menu-link">Service</a></li><li><a href="/zh-cn/basics/plugin.html" class="menu-link">插件</a></li><li><a href="/zh-cn/basics/schedule.html" class="menu-link">定时任务</a></li><li><a href="/zh-cn/basics/extend.html" class="menu-link">框架扩展</a></li><li><a href="/zh-cn/basics/app-start.html" class="menu-link">启动自定义</a></li></ul></dd><dt id="title-Core" style="cursor: pointer;" class="aside-title">核心功能<a id="collapse-icon-Core" class="icon opend"></a></dt><dd id=panel-Core><ul><li><a href="/zh-cn/core/development.html" class="menu-link">本地开发</a></li><li><a href="/zh-cn/core/unittest.html" class="menu-link">单元测试</a></li><li><a href="/zh-cn/core/deployment.html" class="menu-link">应用部署</a></li><li><a href="/zh-cn/core/logger.html" class="menu-link">日志</a></li><li><a href="/zh-cn/core/httpclient.html" class="menu-link">HttpClient</a></li><li><a href="/zh-cn/core/cookie-and-session.html" class="menu-link">Cookie and Session</a></li><li><a href="/zh-cn/core/cluster-and-ipc.html" class="menu-link">多进程模型和进程间通讯</a></li><li><a href="/zh-cn/core/view.html" class="menu-link">模板渲染</a></li><li><a href="/zh-cn/core/error-handling.html" class="menu-link">异常处理</a></li><li><a href="/zh-cn/core/security.html" class="menu-link">安全</a></li><li><a href="/zh-cn/core/i18n.html" class="menu-link">国际化</a></li></ul></dd><dt id="title-Tutorials" style="cursor: pointer;" class="aside-title">教程<a id="collapse-icon-Tutorials" class="icon opend"></a></dt><dd id=panel-Tutorials><ul><li><a href="/zh-cn/tutorials/mysql.html" class="menu-link">MySQL</a></li><li><a href="/zh-cn/tutorials/restful.html" class="menu-link">RESTful API</a></li><li><a href="/zh-cn/tutorials/passport.html" class="menu-link">Passport 鉴权</a></li><li><a href="/zh-cn/tutorials/socketio.html" class="menu-link">Socket.IO</a></li><li><a href="/zh-cn/tutorials/assets.html" class="menu-link">静态资源</a></li><li><a href="/zh-cn/tutorials/typescript.html" class="menu-link">TypeScript</a></li></ul></dd><dt id="title-Advanced" style="cursor: pointer;" class="aside-title">进阶<a id="collapse-icon-Advanced" class="icon opend"></a></dt><dd id=panel-Advanced><ul><li><a href="/zh-cn/advanced/loader.html" class="menu-link">Loader</a></li><li><a href="/zh-cn/advanced/plugin.html" class="menu-link">插件开发</a></li><li><a href="/zh-cn/advanced/framework.html" class="menu-link">框架开发</a></li><li><a href="/zh-cn/advanced/cluster-client.html" class="menu-link">多进程研发模式增强</a></li><li><a href="/zh-cn/advanced/view-plugin.html" class="menu-link">模板插件开发规范</a></li><li><a href="/zh-cn/style-guide.html" class="menu-link">代码风格指南</a></li></ul></dd><dt id="title-Community" style="cursor: pointer;" class="aside-title">社区<a id="collapse-icon-Community" class="icon opend"></a></dt><dd id=panel-Community><ul><li><a href="/zh-cn/plugins/" class="menu-link">内置插件列表</a></li><li><a href="/zh-cn/contributing.html" class="menu-link">如何贡献</a></li><li><a href="/zh-cn/resource.html" class="menu-link">资源</a></li><li><a href="/zh-cn/faq.html" class="menu-link">常见问题</a></li></ul></dd></dl>
</aside>
<script>
var mobileTrigger = document.getElementById('mobileTrigger');
var mobileAside = document.getElementById('mobileAside');

var expandMenu = function(title) {
  // handle icon
  const collapseIcon = document.getElementById('collapse-icon-' + title);
  if (collapseIcon) {
    collapseIcon.className = 'icon opend';
  }
  // handle panelEle
  const panelEle = document.getElementById('panel-' + title);
  if (panelEle) {
    panelEle.className = '';
  }
}

var collapseMenu = function(title) {
  // handle icon
  const collapseIcon = document.getElementById('collapse-icon-' + title);
  if (collapseIcon) {
    collapseIcon.className = 'icon closed';
  }
  // handle panelEle
  const panelEle = document.getElementById('panel-' + title);
  if (panelEle) {
    panelEle.className = 'aside-panel-hidden';
  }
}

mobileAside.onclick = function(e) {
  const targetId = e.target.id;
  if (targetId && (targetId.indexOf('title-') > -1 || targetId.indexOf('collapse-icon-') > -1)) {
    const title = targetId.replace('title-', '').replace('collapse-icon-', '');
    try { 
      // the the browser may have no localStroage or JSON.parse may throw exception.
      const menuInfo = JSON.parse(window.localStorage.getItem('menuInfo'));
        
      // current menu status
      const curClosed = menuInfo[title] ? menuInfo[title].closed : false; // default false

      // change UI
      curClosed ? expandMenu(title) : collapseMenu(title);

      // save menuInfo to localStorage
      menuInfo[title] = { closed: !curClosed } // opposite
      window.localStorage.setItem('menuInfo', JSON.stringify(menuInfo));
    } catch (e) {}
  }
};

mobileTrigger.onclick = function(e) {
  e.preventDefault();
  if (mobileAside.className.indexOf('mobile-show') === -1) {
    mobileAside.className += ' mobile-show';
  } else {
    mobileAside.className = 'toc';
  }
};

(function() {
  // save data to localStorage because the page will refresh when user change the url.
  let menuInfo;
  try { 
    // the the browser may have no localStroage or JSON.parse may throw exception.
    menuInfo = JSON.parse(window.localStorage.getItem('menuInfo'));
    if (!menuInfo) {
      menuInfo = {};
      window.localStorage.setItem('menuInfo', JSON.stringify(menuInfo));
    }
  } catch (e) {
    menuInfo = {}; // default {}
  }

  for (const title in menuInfo) {
    if (menuInfo[title] && menuInfo[title].closed) { // menu in closed status.
      collapseMenu(title);
    } else {
      expandMenu(title);
    }
  }

  // highlight menu
  const pathname = window.location.pathname;
  const selector = `a[href="${pathname}"].menu-link,a[href="${pathname}index.html"].menu-link`;
  const menuItem = mobileAside.querySelector(selector);
  if (menuItem) { menuItem.className += ' highlight'; }
})();
</script>

</div>

  </div>
</body>
<script src="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.js"></script>
<script>
docsearch({
  apiKey: '1561de31a86f79507ea00cdb54ce647c',
  indexName: 'eggjs',
  inputSelector: '#search-query',
});
</script>
<div class="cnzz">
<script src="https://s11.cnzz.com/z_stat.php?id=1261142226&web_id=1261142226" language="JavaScript"></script>
</div>

</html>
